package com.kexio.user.service.impl;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kexio.auth.util.AuthContextUtils;
import com.kexio.common.dto.PageResponse;
import com.kexio.user.dto.LoginLogDTO;
import com.kexio.user.dto.OperationLogDTO;
import com.kexio.user.dto.SecurityLogQueryDTO;
import com.kexio.user.entity.LoginLog;
import com.kexio.user.entity.OperationLog;
import com.kexio.user.mapper.LoginLogMapper;
import com.kexio.user.mapper.OperationLogMapper;
import com.kexio.user.service.SecurityLogService;

import jakarta.servlet.http.HttpServletResponse;

@Service
public class SecurityLogServiceImpl implements SecurityLogService {

    private static final Logger logger = LoggerFactory.getLogger(SecurityLogServiceImpl.class);

    private final LoginLogMapper loginLogMapper;
    private final OperationLogMapper operationLogMapper;

    public SecurityLogServiceImpl(LoginLogMapper loginLogMapper, OperationLogMapper operationLogMapper) {
        this.loginLogMapper = loginLogMapper;
        this.operationLogMapper = operationLogMapper;
    }

    @Override
    public PageResponse<LoginLogDTO> queryLoginLogs(SecurityLogQueryDTO query) {
        long current = query.getCurrent() == null ? 1L : query.getCurrent();
        long size = query.getSize() == null ? 10L : query.getSize();
        Page<LoginLog> page = new Page<>(current, size);
        LocalDateTime start = parseTime(query.getStartDate());
        LocalDateTime end = parseTime(query.getEndDate());
        LambdaQueryWrapper<LoginLog> wrapper = new LambdaQueryWrapper<LoginLog>()
            .like(query.getUsername() != null && !query.getUsername().isEmpty(), LoginLog::getUsername, query.getUsername())
            .ge(start != null, LoginLog::getCreateTime, start)
            .le(end != null, LoginLog::getCreateTime, end)
            .eq(query.getStatus() != null && !query.getStatus().isEmpty(), LoginLog::getStatus, query.getStatus())
            .orderByDesc(LoginLog::getCreateTime);
        loginLogMapper.selectPage(page, wrapper);

        PageResponse<LoginLogDTO> resp = new PageResponse<>();
        resp.setCurrent(page.getCurrent());
        resp.setSize(page.getSize());
        resp.setTotal(page.getTotal());
        java.util.List<LoginLogDTO> data = new java.util.ArrayList<>();
        for (LoginLog log : page.getRecords()) {
            LoginLogDTO dto = new LoginLogDTO();
            dto.setId(log.getId());
            dto.setTenantId(log.getTenantId());
            dto.setUsername(log.getUsername());
            dto.setIp(log.getIp());
            dto.setLocation(log.getLocation());
            dto.setUserAgent(log.getUserAgent());
            dto.setStatus(log.getStatus());
            dto.setMessage(log.getMessage());
            dto.setCreateTime(log.getCreateTime() == null ? null : log.getCreateTime().toString().replace('T', ' '));
            data.add(dto);
        }
        resp.setData(data);
        return resp;
    }

    @Override
    public PageResponse<OperationLogDTO> queryOperationLogs(SecurityLogQueryDTO query) {
        long current = query.getCurrent() == null ? 1L : query.getCurrent();
        long size = query.getSize() == null ? 10L : query.getSize();
        Page<OperationLog> page = new Page<>(current, size);
        LocalDateTime start = parseTime(query.getStartDate());
        LocalDateTime end = parseTime(query.getEndDate());
        LambdaQueryWrapper<OperationLog> wrapper = new LambdaQueryWrapper<OperationLog>()
            .like(query.getModule() != null && !query.getModule().isEmpty(), OperationLog::getModule, query.getModule())
            .like(query.getOperation() != null && !query.getOperation().isEmpty(), OperationLog::getOperation, query.getOperation())
            .like(query.getUsername() != null && !query.getUsername().isEmpty(), OperationLog::getUsername, query.getUsername())
            .ge(start != null, OperationLog::getCreateTime, start)
            .le(end != null, OperationLog::getCreateTime, end)
            .orderByDesc(OperationLog::getCreateTime);
        operationLogMapper.selectPage(page, wrapper);

        PageResponse<OperationLogDTO> resp = new PageResponse<>();
        resp.setCurrent(page.getCurrent());
        resp.setSize(page.getSize());
        resp.setTotal(page.getTotal());
        java.util.List<OperationLogDTO> data = new java.util.ArrayList<>();
        for (OperationLog log : page.getRecords()) {
            OperationLogDTO dto = new OperationLogDTO();
            dto.setId(log.getId());
            dto.setTenantId(log.getTenantId());
            dto.setModule(log.getModule());
            dto.setOperation(log.getOperation());
            dto.setMethod(log.getMethod());
            dto.setParams(log.getParams());
            dto.setResult(log.getResult());
            dto.setDuration(log.getDuration());
            dto.setIp(log.getIp());
            dto.setUserAgent(log.getUserAgent());
            dto.setUserId(log.getUserId());
            dto.setUsername(log.getUsername());
            dto.setCreateTime(log.getCreateTime() == null ? null : log.getCreateTime().toString().replace('T', ' '));
            data.add(dto);
        }
        resp.setData(data);
        return resp;
    }

    @Override
    public void exportLoginLogs(SecurityLogQueryDTO query, HttpServletResponse response) throws IOException {
        response.setContentType("text/csv;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=login_logs.csv");
        var out = response.getOutputStream();
        out.write("id,username,ip,location,status,message,createTime\n".getBytes(StandardCharsets.UTF_8));
        // 简单导出最多10000条
        query.setCurrent(1L); query.setSize(10000L);
        PageResponse<LoginLogDTO> page = queryLoginLogs(query);
        for (LoginLogDTO dto : page.getData()) {
            String line = String.format("%s,%s,%s,%s,%s,%s,%s\n",
                    nv(dto.getId()), nv(dto.getUsername()), nv(dto.getIp()), nv(dto.getLocation()), nv(dto.getStatus()), nv(dto.getMessage()), nv(dto.getCreateTime()));
            out.write(line.getBytes(StandardCharsets.UTF_8));
        }
        out.flush();
    }

    @Override
    public void exportOperationLogs(SecurityLogQueryDTO query, HttpServletResponse response) throws IOException {
        response.setContentType("text/csv;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=operation_logs.csv");
        var out = response.getOutputStream();
        out.write("id,module,operation,method,duration,ip,username,createTime\n".getBytes(StandardCharsets.UTF_8));
        // 简单导出最多10000条
        query.setCurrent(1L); query.setSize(10000L);
        PageResponse<OperationLogDTO> page = queryOperationLogs(query);
        for (OperationLogDTO dto : page.getData()) {
            String line = String.format("%s,%s,%s,%s,%s,%s,%s,%s\n",
                    nv(dto.getId()), nv(dto.getModule()), nv(dto.getOperation()), nv(dto.getMethod()),
                    dto.getDuration() == null ? "" : dto.getDuration().toString(), nv(dto.getIp()), nv(dto.getUsername()), nv(dto.getCreateTime()));
            out.write(line.getBytes(StandardCharsets.UTF_8));
        }
        out.flush();
    }

    @Override
    public void clearLoginLogs() {
        String currentTenantId = AuthContextUtils.getCurrentTenantId();
        logger.warn("清空登录日志: tenantId={}", currentTenantId);
        // 添加租户隔离条件
        LambdaQueryWrapper<LoginLog> wrapper = new LambdaQueryWrapper<>();
        if (currentTenantId != null) {
            wrapper.eq(LoginLog::getTenantId, currentTenantId);
        }
        int deletedCount = loginLogMapper.delete(wrapper);
        logger.info("已清空当前租户登录日志: tenantId={}, deletedCount={}", currentTenantId, deletedCount);
    }

    @Override
    public void clearOperationLogs() {
        String currentTenantId = AuthContextUtils.getCurrentTenantId();
        logger.warn("清空操作日志: tenantId={}", currentTenantId);
        // 添加租户隔离条件
        LambdaQueryWrapper<OperationLog> wrapper = new LambdaQueryWrapper<>();
        if (currentTenantId != null) {
            wrapper.eq(OperationLog::getTenantId, currentTenantId);
        }
        int deletedCount = operationLogMapper.delete(wrapper);
        logger.info("已清空当前租户操作日志: tenantId={}, deletedCount={}", currentTenantId, deletedCount);
    }

    private static LocalDateTime parseTime(String s) {
        if (s == null || s.isEmpty()) return null;
        try {
            DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return LocalDateTime.parse(s, f);
        } catch (Exception ignore) {
            try { return LocalDateTime.parse(s); } catch (Exception e) { return null; }
        }
    }

    private static String nv(String s) { return s == null ? "" : s.replace(',', ' '); }
}


